package system

import (
	"context"
	"fmt"
	"time"

	"admin_template/bff/admin/hertz_gen/base"
	genSysCompany "admin_template/bff/admin/hertz_gen/system/sys_company"
	"admin_template/bff/admin/internal/base/data"
	"admin_template/bff/admin/internal/entity/ent"
	"admin_template/bff/admin/internal/entity/ent/predicate"
	"admin_template/bff/admin/internal/entity/ent/syscompany"
	"admin_template/bff/admin/internal/service/system"

	"gitee.com/byx_darwin/go-tools/kitex/rpc_error"
	"gitee.com/byx_darwin/go-tools/tools"
	toolsTime "gitee.com/byx_darwin/go-tools/tools/time"
	"github.com/pkg/errors"
)

type companyRepo struct {
	*data.Data
	db *ent.SysCompanyClient
}

func NewCompanyRepo(d *data.Data) system.CompanyRepo {
	return &companyRepo{
		Data: d,
		db:   d.DB.SysCompany,
	}
}

func (repo *companyRepo) Add(ctx context.Context,
	req any) error {
	info, ok := req.(*genSysCompany.AddRequest)
	if !ok {
		return rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysCompany.AddRequest"))
	}
	ak := tools.GetRandAk(16)
	sk := tools.RefreshSK(ak)
	err := repo.db.
		Create().
		SetName(info.Name).
		SetAddress(info.Address).
		SetTel(info.Phone).
		SetCode(info.Code).
		SetEmail(info.Email).
		SetAk(ak).SetSk(sk).Exec(ctx)
	if err != nil {
		msg := fmt.Sprintf("sys company create(name:%s code:%s ak:%s)",
			info.Name, info.Code, ak)
		if ent.IsConstraintError(err) {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataRepeat, err)
		} else {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlInsertFailure, err)
		}
	}
	return nil

}

func (repo *companyRepo) UpdateInfo(ctx context.Context,
	req any) error {
	info := req.(*genSysCompany.UpdateRequest)
	err := repo.db.UpdateOneID(int(info.Id)).
		SetName(info.Name).
		SetAddress(info.Address).
		SetEmail(info.Email).
		SetTel(info.Phone).
		SetCode(info.Code).Exec(ctx)
	if err != nil {
		msg := fmt.Sprintf("sys company create(name:%s code:%s)",
			info.Name, info.Code)
		if ent.IsConstraintError(err) {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataRepeat, err)
		} else {
			err = errors.Wrap(err, msg)
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
		}
	}
	return nil
}

func (repo *companyRepo) List(ctx context.Context,
	req any) (int64, int64, int64, []any, error) {
	info, ok := req.(*genSysCompany.ListRequest)
	if !ok {
		return 0, 0, 0, nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysCompany.ListRequest"))
	}
	predicates := make([]predicate.SysCompany, 0)
	switch info.State {
	case 1:
		predicates = append(predicates, syscompany.DeleteTimeNotNil())
	case 2:
		predicates = append(predicates, syscompany.DeleteTimeIsNil())
	}
	sql := repo.db.Query()
	if len(predicates) > 0 {
		sql = sql.Where(predicates...)
	}
	total, err := sql.Count(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("分页查询公司总数失败: "))
		return 0, info.Page, info.Size, nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	if total == 0 {
		return 0, info.Page, info.Size, nil, nil
	}
	offset := (info.Page - 1) * info.Size
	list, err := sql.Select(repo.sqlSelect()...).
		Offset(int(offset)).
		Limit(int(info.Size)).Order(ent.Asc(syscompany.FieldID)).All(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("分页查询公司列表失败: "))
		return 0, info.Page, info.Size, nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	companyList := make([]any, 0, len(list))
	for _, value := range list {
		companyList = append(companyList, repo.genInfo(value))
	}
	return int64(total), info.Page, info.Size, companyList, nil
}

func (repo *companyRepo) All(ctx context.Context,
	req any) ([]any, error) {
	info, ok := req.(*genSysCompany.AllRequest)
	if !ok {
		return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeInvalid,
			errors.New("req not *genSysCompany.AllRequest"))
	}
	predicates := make([]predicate.SysCompany, 0)
	switch info.State {
	case 1:
		predicates = append(predicates, syscompany.DeleteTimeNotNil())
	case 2:
		predicates = append(predicates, syscompany.DeleteTimeIsNil())
	}
	sql := repo.db.Query()
	if len(predicates) > 0 {
		sql = sql.Where(predicates...)
	}
	list, err := sql.Select(repo.sqlSelect()...).Order(ent.Asc(syscompany.FieldID)).All(ctx)
	if err != nil {
		err = errors.Wrap(err,
			fmt.Sprintf("分页查询公司列表失败: "))
		return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	companyList := make([]any, 0, len(list))
	for _, value := range list {
		companyList = append(companyList, repo.genInfo(value))
	}
	return companyList, nil
}

func (repo *companyRepo) StateUpdate(ctx context.Context,
	info *base.StateUpdateRequest) error {
	switch info.State {
	case 0:
		err := repo.db.UpdateOneID(int(info.ID)).SetDeleteTime(time.Now()).Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("更新公司状态失败(ID:%d): ", info.ID))
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
		}
	case 1:
		err := repo.db.UpdateOneID(int(info.ID)).ClearDeleteTime().Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("更新公司状态失败(ID:%d): ", info.ID))
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlUpdateFailure, err)
		}
	case 2:
		err := repo.db.DeleteOneID(int(info.ID)).Exec(ctx)
		if err != nil {
			err = errors.Wrap(err,
				fmt.Sprintf("删除公司失败(ID:%d): ", info.ID))
			return rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlDeleteFailure, err)
		}
	}
	return nil
}

func (repo *companyRepo) One(ctx context.Context,
	id int) (any, error) {
	info, err := repo.db.Query().Where(syscompany.ID(id)).
		Select(repo.sqlSelect()...).First(ctx)
	if err != nil {
		if ent.IsNotFound(err) {
			return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeDataInvalid, err)
		}
		return nil, rpc_error.NewBizStatusError(rpc_error.ErrorTypeSqlQueryFailure, err)
	}
	return repo.genInfo(info), nil
}

func (repo *companyRepo) sqlSelect() []string {
	return []string{
		syscompany.FieldID,
		syscompany.FieldName,
		syscompany.FieldAddress,
		syscompany.FieldEmail,
		syscompany.FieldCode,
		syscompany.FieldAk,
		syscompany.FieldSk,
		syscompany.FieldTel,
		syscompany.FieldDeleteTime,
		syscompany.FieldCreateTime,
		syscompany.FieldUpdateTime,
	}
}

func (repo *companyRepo) genInfo(info *ent.SysCompany) *genSysCompany.Info {
	state := int32(1)
	if info.DeleteTime.Unix() > 0 {
		state = 0
	}
	return &genSysCompany.Info{
		Id:         int64(info.ID),
		Name:       info.Name,
		Address:    info.Address,
		Phone:      info.Tel,
		Email:      info.Email,
		Code:       info.Code,
		State:      state,
		Ak:         info.Ak,
		CreateTime: toolsTime.Format(info.CreateTime.Unix(), "YYYY-MM-DD HH:mm:ss", ""),
		UpdateTime: toolsTime.Format(info.UpdateTime.Unix(), "YYYY-MM-DD HH:mm:ss", ""),
		Sk:         info.Sk,
	}
}

func (repo *companyRepo) Init(ctx context.Context) error {
	info := &genSysCompany.AddRequest{
		Name:    "深圳市南宸科技有限公司",
		Address: "深圳市南山区南头街道莲城社区深南大道10128号南山软件园A1403",
		Phone:   "18675866967",
		Email:   "18675866967@163.com",
		Code:    "sznc",
	}
	return repo.Add(ctx, info)
}
